package com.zpark.evaluate;

import com.zpark.entity.EvaluateData;
import com.zpark.entity.EvaluateReport;
import com.zpark.entity.HistoryData;
import com.zpark.entity.RiskFactor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 输入特征评估模型
 * 通过欧氏距离数学公式完成
 */
public class InputfeatureEvaluate extends Evaluate {

    public InputfeatureEvaluate() {
        super(RiskFactor.INPUTFEATURE);
    }

    @Override
    public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {

        Boolean isRisk = doEval(evaluateData.getInputFeatures(),historyData.getLatestInputFeatures());
        evaluateReport.signReport(getRiskFactor(),isRisk);

        evaluateChain.doChain(evaluateData, historyData, evaluateReport);
    }

    private Boolean doEval(Double[] inputFeatures, List<Double[]> latestInputFeatures) {

        if(latestInputFeatures==null || latestInputFeatures.size()<3){//历史数据必须多余两个
            return false;//没有风险
        }

        //1.计算圆心点
        Double[] yuanXin=new Double[latestInputFeatures.get(0).length];
        for (Double[] latestInputFeature : latestInputFeatures) {

//            System.out.println(Arrays.toString(latestInputFeature));
            for (int i = 0; i < latestInputFeature.length; i++) {
//                System.out.println(latestInputFeature[i]+"**"+yuanXin[i]);
                if(yuanXin[i]==null){
                    yuanXin[i]=0.0;
                }
                yuanXin[i]=yuanXin[i]+latestInputFeature[i];
            }
        }
        for (int i = 0; i < yuanXin.length; i++) {
            yuanXin[i]=yuanXin[i]/latestInputFeatures.size();
        }


        //2.计算历次留存数据和圆心点的欧式距离，然后取一个阈值
        List<Double> ouShiDistances=new ArrayList<>();
        for (Double[] latestInputFeature : latestInputFeatures) {
            //计算每一个留存的数据，和圆心点之间的欧式距离
            ouShiDistances.add(calcOuShiDistance(latestInputFeature,yuanXin));
        }

        //升序排列
        Collections.sort(ouShiDistances);

        Double threshold = ouShiDistances.get(ouShiDistances.size() * 2 / 3);//阈值


        //3.计算评估数据和圆心点的欧式距离
        double value = calcOuShiDistance(inputFeatures, yuanXin);

        //4.如果计算的欧式距离大于阈值，就认定有风险
        return value>threshold;
    }

    /**
     * 计算欧式距离
     * @param x 一个点
     * @param y 另外一个点
     * @return
     */
    private double calcOuShiDistance(Double[] x,Double[] y){

        //根据欧式距离公式完成计算
        double sum =0.0;
        for (int i = 0; i < x.length; i++) {
            sum+=Math.pow((x[i]-y[i]),2);
        }
        return Math.sqrt(sum);
    }
}
